/******************* (C) COPYRIGHT 2022 CHU WEI TENG ***********************************************
**
**------------------ File Info ---------------------------------------------------------------------
** File name:      dwin_trans.c
** modified Date:  2022-05-10
** Last Version:   V1.0
** Descriptions: 
** 
**------------------ Modfication  log --------------------------------------------------------------
** Date       Who            Rev        Comments
----------------------------------------------------------------------------------------------------
   22/05/10  chuwt@qq.com    V1.00      created

****************************************************************************************************/
#include <string.h>
#include "dwin_trans.h"
#include "dwin_adapt.h"

/* Private define----------------------------------------------------------------------------------*/
#define DWIN_USING_HEADH        (0x5A)
#define DWIN_USING_HEADL        (0xA5)

#define DWIN_REG_WRITE          (0x80)
#define DWIN_REG_READ           (0x81)
#define DWIN_VAR_WRITE          (0x82)
#define DWIN_VAR_READ           (0x83)

#define CMD_TIMEOUT             (50)
#define SED_TIMEOUT             (200)

/* Private typedef---------------------------------------------------------------------------------*/

/* Private variables-------------------------------------------------------------------------------*/

static dwin_trans_t*  dwin_trans = NULL;

/* public function---------------------------------------------------------------------------------*/

ret_t dwin_reg_read(uint16_t addr, void* data, uint8_t len)
{
    return_value_if_fail(data, RET_BAD_PARAMS);

    ret_t result = RET_FAIL;

    if (dwin_trans != NULL)
    {
        data_pack_t  pack = {0};
        uint16_t     cmd_timeout = CMD_TIMEOUT;
        uint16_t     send_timeout = SED_TIMEOUT;
        uint32_t     ret = 0;

        pack.head_high = DWIN_USING_HEADH;
        pack.head_low = DWIN_USING_HEADL;
        pack.length = 4;
        pack.cmd = DWIN_REG_READ;    
        pack.addr_high = addr >> 8;
        pack.addr_low = addr;
        pack.data[0] = len;

        while (send_timeout > 0)
        {
            ret = dwin_fifo_push(dwin_trans->send_fifo, &pack);
            if (ret != 0)
            {
                break;
            }
            dwin_delay(5);
            send_timeout--;
        }
        
        if (ret != 0)
        {
            dwin_fifo_clear(dwin_trans->cmd_fifo);
            dwin_trans->dwin_state = TRANS_CMD_RECV;

            while (cmd_timeout > 0)
            {
                if (dwin_fifo_pop(dwin_trans->cmd_fifo, &pack))
                {
                    if (pack.length == (len + 4) && pack.cmd == DWIN_REG_READ && 
                        pack.addr_high == (addr >> 8) && pack.addr_low == (addr & 0xFF) && 
                        pack.data[0] == len)
                    {
                        memcpy(data, &pack.data[1], len);
                        result = RET_OK;
                    }
                    break;
                }
                dwin_delay(10);
                cmd_timeout--;
            }
        }
    }
    return result;
}

ret_t dwin_reg_write(uint16_t addr, void* data, uint8_t len)
{
    return_value_if_fail(data, RET_BAD_PARAMS);

    ret_t result = RET_FAIL;

    if (dwin_trans != NULL)
    {
        data_pack_t  pack = {0};
        uint16_t     send_timeout = SED_TIMEOUT;
        uint32_t     ret = 0;

        pack.head_high = DWIN_USING_HEADH;
        pack.head_low = DWIN_USING_HEADL;
        pack.length = len + 3;
        pack.cmd = DWIN_REG_WRITE;    
        pack.addr_high = addr >> 8;
        pack.addr_low = addr;
        memcpy(&pack.data[0], data, len);

        while (send_timeout > 0)
        {
            ret = dwin_fifo_push(dwin_trans->send_fifo, &pack);
            if (ret != 0)
            {
                break;
            }
            dwin_delay(5);
            send_timeout--;
        }
        
        if (ret != 0)
        {
            result = RET_OK;
        }
    }
    return result;
}

ret_t dwin_var_read(uint16_t addr, void* data, uint8_t len)
{
    return_value_if_fail(data, RET_BAD_PARAMS);

    ret_t result = RET_FAIL;

    if (dwin_trans != NULL)
    {
        data_pack_t  pack = {0};
        uint16_t     cmd_timeout = CMD_TIMEOUT;
        uint16_t     send_timeout = SED_TIMEOUT;
        uint32_t     ret = 0;

        pack.head_high = DWIN_USING_HEADH;
        pack.head_low = DWIN_USING_HEADL;
        pack.length = 4;
        pack.cmd = DWIN_VAR_READ;    
        pack.addr_high = addr >> 8;
        pack.addr_low = addr;
        pack.data[0] = len;

        while (send_timeout > 0)
        {
            ret = dwin_fifo_push(dwin_trans->send_fifo, &pack);
            if (ret != 0)
            {
                break;
            }
            dwin_delay(5);
            send_timeout--;
        }
        if (ret != 0)
        {
            dwin_fifo_clear(dwin_trans->cmd_fifo);
            dwin_trans->dwin_state = TRANS_CMD_RECV;
            
            while (cmd_timeout > 0)
            {
                if (dwin_fifo_pop(dwin_trans->cmd_fifo, &pack))
                {
                    if (pack.length == ((len << 1) + 4) && pack.cmd == DWIN_VAR_READ && 
                        pack.addr_high == (addr >> 8) && pack.addr_low == (addr & 0xFF) && 
                        pack.data[0] == len)
                    {
                        memcpy(data, &pack.data[1], len << 1);
                        result = RET_OK;
                    }
                    break;
                }
                dwin_delay(10);
                cmd_timeout--;
            }
        }
    }
    return result;
}

ret_t dwin_var_write(uint16_t addr, void* data, uint8_t len)
{
    return_value_if_fail(data, RET_BAD_PARAMS);

    ret_t result = RET_FAIL;

    if (dwin_trans != NULL)
    {
        data_pack_t  pack = {0};
        uint16_t     send_timeout = SED_TIMEOUT;
        uint32_t     ret = 0;

        pack.head_high = DWIN_USING_HEADH;
        pack.head_low = DWIN_USING_HEADL;
        pack.length = (len << 1) + 3;
        pack.cmd = DWIN_VAR_WRITE;    
        pack.addr_high = addr >> 8;
        pack.addr_low = addr;
        memcpy(&pack.data[0], data, len << 1);
        
        while (send_timeout > 0)
        {
            ret = dwin_fifo_push(dwin_trans->send_fifo, &pack);
            if (ret != 0)
            {
                break;
            }
            dwin_delay(5);
            send_timeout--;
        }
        
        if (ret != 0)
        {
            result = RET_OK;
        }
    }
    return result;
}

dwin_trans_t* dwin_trans_init(void)
{
    if (dwin_trans == NULL)
    {
        dwin_trans = dwin_malloc(sizeof(dwin_trans_t));
        
        if (dwin_trans != NULL)
        {
            memset(dwin_trans, 0, sizeof(sizeof(dwin_trans_t)));
            
            dwin_trans->send_fifo = dwin_fifo_create(sizeof(data_pack_t), 8);
            dwin_trans->data_fifo = dwin_fifo_create(sizeof(data_pack_t), 2);
            dwin_trans->cmd_fifo = dwin_fifo_create(sizeof(data_pack_t), 1);    

            if (!dwin_trans->send_fifo || !dwin_trans->data_fifo || !dwin_trans->data_fifo)
            {
                dwin_fifo_destory(dwin_trans->send_fifo);
                dwin_fifo_destory(dwin_trans->data_fifo);
                dwin_fifo_destory(dwin_trans->cmd_fifo);
                dwin_free(dwin_trans);  
                dwin_trans = NULL;    
            }  
        }
    }
    
    return dwin_trans;
}

ret_t dwin_trans_fini(void)
{
    return_value_if_fail(dwin_trans, RET_FAIL);

    dwin_fifo_destory(dwin_trans->send_fifo);
    dwin_fifo_destory(dwin_trans->data_fifo);
    dwin_fifo_destory(dwin_trans->cmd_fifo);
    dwin_free(dwin_trans);  
    dwin_trans = NULL;    

    return RET_OK;
}

dwin_trans_t* dwin_trans_get(void)
{
    return dwin_trans;
}

ret_t dwin_trans_recv(dwin_trans_t* trans, void* data, uint16_t len)
{
    return_value_if_fail(trans, RET_BAD_PARAMS);
    return_value_if_fail(data, RET_BAD_PARAMS); 

    ret_t result = RET_FAIL; 

    if (len >= 6 && len <= sizeof(data_pack_t))
    {
        uint8_t* p_data =  data;

        if (p_data[0] == DWIN_USING_HEADH && p_data[1] == DWIN_USING_HEADL)
        {
            data_pack_t pack = {0};
            memcpy(&pack, data, len);

            switch (trans->dwin_state)
            {
                case TRANS_DATA_RECV: 
                {
                    dwin_fifo_push(trans->data_fifo, &pack);
                }break;
                
                case TRANS_CMD_RECV:
                {
                    dwin_fifo_push(trans->cmd_fifo, &pack);
                    trans->dwin_state = TRANS_DATA_RECV;
                }break;
            }

            result = RET_OK;  
        }
    }

    return result;
}
